#include "libpcb.h"

void _inicializarVariables(char * instruccion, Proceso * p);
int _esAsignacion(char * instruccion);
int _procesarAsignacion(char * instruccion,Proceso * p);
void _cambiarValorVariable(const char * nombre, int valor, Proceso * p);
int _getValorVariable(const char * nombre, Proceso * p);
void imprimirVariables(Proceso * p, FILE * stream);
int _getTiempoProceso(char * instruccion);
int _esIO(char * instruccion);
int _esFuncion(char * instruccion);
void _agregarStack(char * nombre, int linea, struct tStack * stack);
int _getLineaFuncion(char * nombre, Proceso * p);
void _entrarFuncion(char * instruccion, Proceso * p);
int _getLineaStack(char * funcion,struct tStack * stack);
void _finFuncion(char * instruccion, Proceso * p);
void imprimirVariablesOut(Proceso * p);
int _calcularServicio(Proceso * p, int linea, int * exit,int * recursivo);
int _getDuracionAsignacion(char * instruccion);
int _entrarFuncionCalcularServicio(char * instruccion, int linea, Proceso * p);
int _finFuncionCalcularServicio(char * instruccion, Proceso * p);

Proceso * nuevoProceso(const char * archivo) {
	FILE * fp;
	char buffer[BUFFERSIZE];
	int cantLineas;
	int i = 1;
	int lenBuffer;
	int j,k;
	
	if((fp = fopen(archivo,"r")) == NULL) {
		return NULL;
	}
	
	Proceso * p = (Proceso *)reservar(sizeof(Proceso));
	
	//Inicializo en NULL el codigo
	p->codigo = (char **)reservar(sizeof(char *) * i);
	p->codigo[0] = NULL;
	p->instruccionActual = 0;
	
	//Inicializo todo a \0
	for(j=0;j<MAXCANTSTACK;j++) {
		for(k=0;k<MAXNOMBREVAR;k++) {
			p->_stackCalcServ[j].nombre[k] = '\0';
		}
	}
	for(j=0;j<MAXCANTSTACK;j++) {
		for(k=0;k<MAXNOMBREVAR;k++) {		
			p->stack[j].nombre[k] = '\0';
		}
	}
	for(j=0;j<MAXCANTVAR;j++) {
		for(k=0;k<MAXNOMBREVAR;k++) {
			p->variables[j].nombre[k] = '\0';
		}
	}
	
	cantLineas = 0;
	//Lee todo el archivo
	while(fgets(buffer,BUFFERSIZE,fp) != NULL) {
		if(buffer[0] != '\n') {
			i++;
			p->codigo = (char **)realloc(p->codigo,sizeof(char *)* i);
			lenBuffer = strlen(buffer);
			p->codigo[cantLineas] = (char *)reservar(lenBuffer+1);
			strcpy(p->codigo[cantLineas],buffer);
			//Remplaza \n por \0
			if(p->codigo[cantLineas][lenBuffer-1] == '\n') {
				p->codigo[cantLineas][lenBuffer-1] = '\0';
			}
			cantLineas++;
		}
	}
	p->cantInstrucciones = cantLineas;
	fclose(fp);
	
	return p;
}

int calcularServicio(Proceso * p, int cpuWait) {
	int tiempo = 0,exit=0;
	char * instruccion;
	int linea = p->instruccionActual;
	int i;
	
	while(!exit) {
		
		instruccion = (char *)reservar(strlen(p->codigo[linea])+1); 
		strcpy(instruccion,p->codigo[linea]);
		
		//Operacion de asignacion
		if(_esAsignacion(instruccion)) {
			i = _getTiempoProceso(instruccion);
			if(i!=0) {
				tiempo += i;
			}
			else {
				tiempo += cpuWait;
			}
		}
		//Operacion de IO
		else if(_esIO(instruccion)) {
			exit = 1;
			return tiempo;	
		}
		//Entro a una funcion
		else if(_esFuncion(instruccion)) {	
			linea = _entrarFuncionCalcularServicio(instruccion,linea,p);
		}
		//Termina una funcion
		else if(strstr(instruccion,"fin_funcion")) {
			linea = _finFuncionCalcularServicio(instruccion,p);	
		}
		//Termina el programa
		else if(strstr(instruccion,"fin_programa")) {
			exit = 1;
			return tiempo;
		}
		linea++;
		free(instruccion);
	}
	return tiempo;
}

int procesar(Proceso * p, int cpuWait, int ioWait, int * ret) {
	char * instruccion = (char *)reservar(strlen(p->codigo[p->instruccionActual])+1); 
	strcpy(instruccion,p->codigo[p->instruccionActual]);
	
	int i;
	int tiempoSleep = cpuWait;
	
	//Es un comentario, forget it	
	if(instruccion[0] == '#'){
	}
	//Declaracion de variables
	else if(strstr(instruccion,"variables") != NULL) {
		_inicializarVariables(instruccion,p);
	}
	//Comienzo del programa. No hago nada, paso a la siguiente instruccion
	else if(strcmp(instruccion,"comienzo_programa") == 0) {
	}
	//Operacion de asignacion
	else if(_esAsignacion(instruccion)) {
		i = _procesarAsignacion(instruccion,p);
		if(i != 0) {
			tiempoSleep = i;
		}
	}
	//Operacion de IO
	else if(_esIO(instruccion)) {
		tiempoSleep = _getTiempoProceso(instruccion);
		*ret = tiempoSleep;
		tiempoSleep = ioWait;		
	}
	//Entro a una funcion
	else if(_esFuncion(instruccion)) {
		_entrarFuncion(instruccion,p);
	}
	//Comienza funcion. No hace nada
	else if(strstr(instruccion,"comienzo_funcion"))	{
	}
	//Termina una funcion
	else if(strstr(instruccion,"fin_funcion")) {
		_finFuncion(instruccion,p);
	}
	//Termina el programa
	else if(strstr(instruccion,"fin_programa")) {
		return 1;
	}
	
	p->instruccionActual++;
	if(tiempoSleep != 0) {
		sleep(tiempoSleep);
	}
	
	return 0;
	
}

void _finFuncion(char * instruccion, Proceso * p) {
	char funcion[MAXNOMBREVAR];
	int linea;
	
	sscanf(instruccion,"fin_funcion %s",funcion);
	linea = _getLineaStack(funcion,p->stack);
	p->instruccionActual = linea;
}

int _finFuncionCalcularServicio(char * instruccion, Proceso * p) {
	char funcion[MAXNOMBREVAR];
	
	sscanf(instruccion,"fin_funcion %s",funcion);
	return _getLineaStack(funcion,p->_stackCalcServ);
}

int _getLineaStack(char * funcion, struct tStack * stack) {
	int ret;
	int i = 0;
	//Obtengo el primer elemento del stack que es la ultima funcion a la cual entre
	ret = stack[0].valor;
	
	//Muevo todo al principio
	while(stack[i+1].nombre[0] != '\0') {
		stack[i] = stack[i+1];
		i++;
	}
	stack[i].nombre[0] = '\0';
	
	return ret;
}
void _entrarFuncion(char * instruccion, Proceso * p) {
	char * nombreFuncion = NULL;
	int lineaFuncion;
	
	nombreFuncion = strtok(instruccion,"(");
	lineaFuncion = _getLineaFuncion(nombreFuncion,p);
	_agregarStack(nombreFuncion,p->instruccionActual,p->stack);
	p->instruccionActual = lineaFuncion;
}

int _entrarFuncionCalcularServicio(char * instruccion, int linea, Proceso * p) {
	char * nombreFuncion = NULL;
	
	nombreFuncion = strtok(instruccion,"(");
	_agregarStack(nombreFuncion,linea,p->_stackCalcServ);
	return _getLineaFuncion(nombreFuncion,p);
}

void _agregarStack(char * nombre, int linea, struct tStack * stack) {
	int i = 0;
	
	//Busco el ultimo elemento
	while(stack[i].nombre[0] != '\0') {
		i++;
	}
	//Muevo todo una posicion
	while( i >= 0 ) {
		stack[i+1] = stack[i];
		i--; 
	}
	//Guardo el elemento al principio
	strcpy(stack[0].nombre,nombre);
	stack[0].valor = linea;
}

int _getLineaFuncion(char * nombre, Proceso * p) {
	int i = 0;
	char * linea = (char *)reservar(MAXNOMBREVAR+20);
	
	sprintf(linea,"comienzo_funcion %s",nombre);
	
	while(i < p->cantInstrucciones) {
		if(strstr(p->codigo[i],linea)) {
			return i;
		}
		i++;
	}
	return 0;
}

int _esFuncion(char * instruccion) {
	//No es IO y es otro tipo de funcion
	if(strstr(instruccion,"io()") == NULL && strstr(instruccion,"()") != NULL && instruccion[0] != '#') {
		return 1;
	}
	else {
		return 0;
	}
}
int _getTiempoProceso(char * instruccion) {
	int ret = 0;
	char * num = NULL;
	
	strtok(instruccion,";");
	if((num = strtok(NULL,"")) != NULL) {
		ret = atoi(num);
	}
	return ret;
}

int _esIO(char * instruccion) {
	if(strstr(instruccion,"io()")) {
		return 1;
	}
	else {
		return 0;
	}
}
int _esAsignacion(char * instruccion) {
	if(strchr(instruccion,'=') != NULL) {
		return 1;
	} 
	else {
		return 0;
	}
}

int _procesarAsignacion(char * instruccion,Proceso * p) {
	char * v1 = NULL;
	char * v2 = NULL;
	char * v3 = NULL;
	char * wait = NULL;
	int valor1=0,valor2=0,valor3=0,cpuWait=0;
	int suma;
	
	if(strchr(instruccion,'+')) {
		suma=1;
	}
	else {
		suma=0;
	}
	
	//Obtengo el valor de las variables 
	v1=strtok(instruccion,"=");
	
	if((v2 = strtok(NULL,"+-")) != NULL) {
    	if(isdigit((int)v2[0])) {
    		valor2=atoi(v2);
    	}
    	else {
    		valor2=_getValorVariable(v2,p);
    	}
    }
    if((v3 = strtok(NULL,";")) != NULL) {
    	if(isdigit((int)v3[0])) {
    		valor3=atoi(v3);
    	}
    	else {
    		valor3=_getValorVariable(v3,p);
    	}
    }
	if((wait = strtok(NULL,"")) != NULL) {
    	if(isdigit((int)wait[0])) {
    		cpuWait=atoi(wait);
    	}
    }
    
    if(suma) {
    	valor1 = valor2+valor3;
    }
    else {
    	valor1 = valor2-valor3;
    }
    
    //Ahora hago la asignacion
    _cambiarValorVariable(v1,valor1,p);
    
    //imprimirVariablesOut(p);
    
    return cpuWait;
 
}

void _cambiarValorVariable(const char * nombre, int valor, Proceso * p) {
	int i = 0;
	while(p->variables[i].nombre[0] != '\0' && i < MAXCANTVAR) {
		if(strcmp(nombre,p->variables[i].nombre) == 0) {
			p->variables[i].valor = valor;
			return;
		}
		i++;
	}
}

int _getValorVariable(const char * nombre, Proceso * p) {
	int i = 0;
	while(p->variables[i].nombre[0] != '\0' && i < MAXCANTVAR) {
		if(strcmp(nombre,p->variables[i].nombre) == 0) {
			return p->variables[i].valor;
		}
		i++;
	}
	return 0;
}



void _inicializarVariables(char * instruccion, Proceso * p) {
	char * variables;
	int i,k;
	int cantVariables = 0;
	
	//Separar el string por ' '
	variables = strtok(instruccion," ");
	variables = strtok(NULL," ");
	
	
	//Crea las variables
	//No trates de entenderlo. Simplemente funciona
	//Se podría haber resuelto con strtok pero hoy estoy un poco demente
	i=0;
	while(variables[i] != '\0') {
		k=0;
		while(variables[i] != ',' && variables[i] != '\0') {
			p->variables[cantVariables].nombre[k] = variables[i];
			k++;
			i++;
		}
		if(variables[i] != '\0') {
			i++;
		}
		cantVariables++;
	}
}

void imprimirVariables(Proceso * p, FILE * fp) {
	int i = 0;
	while(p->variables[i].nombre[0] != '\0') {
		fprintf(fp, "%s=%d\n",p->variables[i].nombre,p->variables[i].valor);
		i++;
	}
}

void imprimirVariablesOut(Proceso * p) {
	int i = 0;
	while(p->variables[i].nombre[0] != '\0') {
		printf("%s=%d\n",p->variables[i].nombre,p->variables[i].valor);
		i++;
	}
}

char * getFileName(const char * path) {
	char * filePath;
	char * fileName = NULL;
	char * tmp;
	char * ret;
	size_t lenFileName;

	ret = (char*) reservar(sizeof(char) * (strlen(path)+1));
	filePath = (char*)reservar(sizeof(char)*(strlen(path)+1));
	strcpy(filePath,path);
	fileName = strtok(filePath,"/");
	while((tmp = strtok(NULL,"/")) != NULL) {
		if(tmp != NULL) {
			fileName = tmp;
		}
	}
	lenFileName = strlen(fileName)-strlen("ansisop")-1;
	strncpy(ret,fileName,lenFileName);
	ret[lenFileName] = '\0';
	free(filePath);
	return ret;
}

void serializar(Proceso * p, const char * path, char * pathImagen) {
	FILE * fp;
	char filePath[100];
	char * fileName;
	int i;
	
	if(mkdir(pathImagen, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) == -1) {
		if(errno != EEXIST) {
			//Exit de todo los procesos
		}
	}
	fileName = getFileName(path);
	sprintf(filePath,"%s%s.imagen",pathImagen,fileName);
	if((fp = fopen(filePath,"w")) == NULL) {
		//Exit de todos los procesos
	}
	
	//Le sumo uno porque la instruccion actual es el indice del array de instrucciones
	fprintf(fp,"PC=%d\n",p->instruccionActual+1);
	fprintf(fp,"Lineas=%d\n",p->cantInstrucciones);
	fprintf(fp,"-------Codigo del programa-----\n");
	for(i=0;i < p->cantInstrucciones;i++) {
		fprintf(fp,"%s\n",p->codigo[i]);	
	}
	fprintf(fp,"-------------------------------\n");
	fprintf(fp,"-------Estado Variables--------\n");
	imprimirVariables(p,fp);
	fprintf(fp,"-------------------------------\n");
	fprintf(fp,"-------Estado Pila-------------\n");
	for(i=0;p->stack[i].nombre[0] != '\0';i++) {
		fprintf(fp,"%d,%s\n",p->stack[i].valor,p->stack[i].nombre);
	}
	fprintf(fp,"-------------------------------\n");
	
	fclose(fp);
}

int fileExist(const char * archivo) {
	FILE * fp;
	
	if((fp = fopen(archivo,"r")) == NULL) {
		return 0;
	}
	else {
		fclose(fp);
		return 1;
	}
}

Proceso * deserealizar(const char * path, char * pathImagen) {
	char filePath[100];
	char * fileName;
	char buffer[1000];
	Proceso * p;
	FILE * fp;
	int i,fin = 0;
	int j=1;
	int k=0;
	
	
	fileName = getFileName(path);
	sprintf(filePath,"%s%s.imagen", pathImagen,fileName);
	if((fp = fopen(filePath,"r")) == NULL) {
		//Exit de todos los procesos
	}
	
	p = (Proceso *)reservar(sizeof(Proceso));
	
	//Inicializo en NULL el codigo
	p->codigo = (char **)reservar(sizeof(char *));

	//Inicializo todo a \0
	for(j=0;j<MAXCANTSTACK;j++) {
		for(k=0;k<MAXNOMBREVAR;k++) {
			p->_stackCalcServ[j].nombre[k] = '\0';
		}
	}
	for(j=0;j<MAXCANTSTACK;j++) {
		for(k=0;k<MAXNOMBREVAR;k++) {
			p->stack[j].nombre[k] = '\0';
		}
	}
	for(j=0;j<MAXCANTVAR;j++) {
		for(k=0;k<MAXNOMBREVAR;k++) {
			p->variables[j].nombre[k] = '\0';
		}
	}
	
	fscanf(fp,"PC=%d\n",&p->instruccionActual);
	//Le resto uno a instruccionActual para que corresponda al indice del array de codigo
	p->instruccionActual--;
	fscanf(fp,"Lineas=%d\n",&p->cantInstrucciones);
	fscanf(fp,"-------Codigo del programa-----\n");
	
	for(i=0,j=1;i < p->cantInstrucciones;i++) {
		j++;
		p->codigo = (char **)realloc(p->codigo,sizeof(char *) * j);
		fscanf(fp,"%[^\n]\n",buffer);
		p->codigo[i] = (char*)reservar(strlen(buffer)+1);
		strcpy(p->codigo[i],buffer);			
	}
	fscanf(fp,"-------------------------------\n");
	fscanf(fp,"-------Estado Variables--------\n");
	i=0;
	while(!fin) {
		fgets(buffer,1000,fp);
		if(strchr(buffer,'=')) {
			sscanf(buffer,"%[^=]=%d\n",p->variables[i].nombre,&p->variables[i].valor);
			i++;
		}
		else {
			fin=1;
		}
	}
	//fscanf(fp,"-------------------------------\n");
	fscanf(fp,"-------Estado Pila-------------\n");
	i=0;
	fin=0;
	while(!fin) {
		fgets(buffer,1000,fp);
		if(strchr(buffer,',')) {
			sscanf(buffer,"%d,%[^\n]\n",&p->stack[i].valor,p->stack[i].nombre);
			i++;
		}
		else {
			fin=1;
		}
	}
	//scanf(fp,"-------------------------------\n");
	
	fclose(fp);
	remove(filePath);
	return p;
}

int spn(Tabla * tabla, int pid1, int pid2, int cpuWait) {
	int timeServPid1 = calcularServicio(getProceso(tabla,pid1),cpuWait);
	int timeServPid2 = calcularServicio(getProceso(tabla,pid2),cpuWait);
	if(timeServPid1 <= timeServPid2) {
		return 1;
	}
	else {
		return 0;
	}
}

int hrrn(Tabla * tabla, int pid1, int pid2, int cpuWait) {
	int timeServPid1,timeServPid2;
	long timeWaitingPid1,timeWaitingPid2;
	double hrrnTimePid1,hrrnTimePid2;
	time_t t = time(NULL);

	timeServPid1 = calcularServicio(getProceso(tabla,pid1),cpuWait);
	timeServPid2 = calcularServicio(getProceso(tabla,pid2),cpuWait);
	
	timeWaitingPid1 =  (long)t - (long)getTimeStamp(tabla,pid1);
	timeWaitingPid2 =  (long)t - (long)getTimeStamp(tabla,pid2);
	
	hrrnTimePid1 = (timeWaitingPid1+timeServPid1)/timeServPid1;
	hrrnTimePid2 = (timeWaitingPid2+timeServPid2)/timeServPid2;

	if(hrrnTimePid1 >= hrrnTimePid2) {
		return 1;
	}
	else {
		return 0;
	}
	//printf("El tiempo de hrrn para pid %i es de %f \n",pid,hrrnTime);
	
}
